//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// Copyright (C) 2012 forgiven
//  
// console library
//
// forgiven 2012.06.14
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#ifndef __UTILITY_CONSOLE_HPP__
#define __UTILITY_CONSOLE_HPP__
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <string>
#include <stdio.h>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/function.hpp>

#include "singleton.hpp"

namespace core
{
	typedef boost::function<void (std::string& cmd_input)> HandleConsoleInputType;

	class Console : public core::Singleton<Console>
	{
		friend core::Singleton<Console>;
	public:
		Console()
			: is_running_(false)
		{}
		~Console()
		{
#ifndef _CONSOLE
			FreeConsole();
#endif
		}
	public:
		bool Initialize()
		{
			if (is_running_)
				return false;

#ifndef _CONSOLE
			if (!AllocConsole())
				return false;
#endif
			*stdout = *_fdopen(_open_osfhandle(reinterpret_cast<LONG>(GetStdHandle(STD_OUTPUT_HANDLE)), _O_TEXT), "r+");
			*stderr = *_fdopen(_open_osfhandle(reinterpret_cast<LONG>(GetStdHandle(STD_ERROR_HANDLE)), _O_TEXT), "r+");
			*stdin = *_fdopen(_open_osfhandle(reinterpret_cast<LONG>(GetStdHandle(STD_INPUT_HANDLE)), _O_TEXT), "r+");
			return true;
		}
		bool Start(HandleConsoleInputType handle_console_input)
		{
			if (is_running_)
				return false;

			handle_console_input_ = handle_console_input;
			boost::thread(boost::bind(&Console::WorkThread, this)).swap(thread_);

			return true;
		}
		void Stop()
		{
			if (!is_running_)
				return;

			is_running_ = false;

			INPUT_RECORD ir[2];
			ir[0].EventType = KEY_EVENT;
			ir[0].Event.KeyEvent.bKeyDown = TRUE;
			ir[0].Event.KeyEvent.dwControlKeyState = 288;
			ir[0].Event.KeyEvent.uChar.AsciiChar = 13;
			ir[0].Event.KeyEvent.wRepeatCount = 1;
			ir[0].Event.KeyEvent.wVirtualKeyCode = 13;
			ir[0].Event.KeyEvent.wVirtualScanCode = 28;
			ir[1].EventType = KEY_EVENT;
			ir[1].Event.KeyEvent.bKeyDown = FALSE;
			ir[1].Event.KeyEvent.dwControlKeyState = 288;
			ir[1].Event.KeyEvent.uChar.AsciiChar = 13;
			ir[1].Event.KeyEvent.wRepeatCount = 1;
			ir[1].Event.KeyEvent.wVirtualKeyCode = 13;
			ir[1].Event.KeyEvent.wVirtualScanCode = 28;
			DWORD write_len = 0;
			WriteConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &write_len);

			if (boost::this_thread::get_id() != thread_.get_id())
			{
				DWORD exit_code;
				GetExitCodeThread(thread_.native_handle(), &exit_code);
				if (STILL_ACTIVE != exit_code)
				{
					thread_.join();
				}
			}
		}
		void WaitForConsoleStop()
		{
			thread_.join();
		}
	private:
		void WorkThread()
		{
			is_running_ = true;

			mutex_.lock();

			while(is_running_)
			{
				std::cout << ">>" << std::flush;
				getline(std::cin, input_);
				if (!is_running_)
					break;

				if (!handle_console_input_.empty())
					handle_console_input_(input_);

				input_.clear();
			}
			mutex_.unlock();
		}
	public:
		inline bool is_running() { return is_running_; }
	private:
		bool is_running_;
		std::string input_;
		boost::mutex mutex_;
		boost::thread thread_;
		HandleConsoleInputType handle_console_input_;
	};
}//namespace core
#define sConsole core::Console::GetInstance()
#endif//__UTILITY_CONSOLE_HPP__